//------------------------------------------------------------------------------
// <copyright file="RpcMessageDispatcher.cpp" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------

#pragma once

#include <ppl.h>
#include <map>
#include "RpcSocketTransportMessage.h"

namespace Microsoft { namespace Internal { namespace GamesTest { namespace Rpc { namespace Server { namespace Ut
{
	class RpcMessageDispatcherTests;
	class WinsockListeningSocketTests;
	class WinRtStringSocketTests;
	class RpcServerImplTests;
}}}}}}

namespace Microsoft { namespace Internal { namespace GamesTest { namespace Rpc { namespace Server
{
	class IRpcMessageHandler;
	class IRpcChannel;

	class RpcMessageDispatcher
	{
	private:
		friend class Microsoft::Internal::GamesTest::Rpc::Server::Ut::RpcMessageDispatcherTests;
		friend class Microsoft::Internal::GamesTest::Rpc::Server::Ut::WinsockListeningSocketTests;
		friend class Microsoft::Internal::GamesTest::Rpc::Server::Ut::WinRtStringSocketTests;
		friend class Microsoft::Internal::GamesTest::Rpc::Server::Ut::RpcServerImplTests;

		static concurrency::critical_section s_socketListenerLock;

		/// <summary>
		/// Comparator class used to sort GUIDs in an STL container.
		/// </summary>
		class GuidComparator
		{
		public:
			bool operator()(const GUID& first, const GUID& second) const
			{
				if(first.Data1 < second.Data1)
				{
					return true;
				}
				else if(first.Data1 > second.Data1)
				{
					return false;
				}

				if(first.Data2 < second.Data2)
				{
					return true;
				}
				else if(first.Data2 > second.Data2)
				{
					return false;
				}

				if(first.Data3 < second.Data3)
				{
					return true;
				}
				else if(first.Data3 > second.Data3)
				{
					return false;
				}

				for (int i = 0; i < 8; i++)
				{
					if(first.Data4[i] < second.Data4[i])
					{
						return true;
					}
					else if(first.Data4[i] > second.Data4[i])
					{
						return false;
					}
				}

				return false;
			}
		};

		typedef std::map<GUID, IRpcMessageHandler*, GuidComparator> MessageHandlerTable;
		static MessageHandlerTable s_activeMessageHandlers;

	public:
		/// <summary>
		/// Register a given request handler to be invoked when a message with the given server Id is received.
		/// </summary>
		/// <param name="serverId">The Id of the server to associate with the given message handler.</param>
		/// <param name="port">The port of the server to associate with the given message handler.</param>
		/// <param name="messageHandler">The message handler to be invoked when a message with the given server Id is received.</param>
		static GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT RegisterMessageHandler(const GUID& serverId, int port, IRpcMessageHandler* messageHandler);

		/// <summary>
		/// Unregisters the handler associated with the given server Id.
		/// </summary>
		/// <param name="serverid">The Id of the server to unregister.</param>
		static GAMESTEST_ANNOTATE_HRESULT_METHOD HRESULT UnregisterMessageHandler(const GUID& serverId);

	protected:
		/// <summary>
		/// This method is invoked when a channel indicates that a message has been received.
		/// </summary>
		/// <param name="channel">The channel on which the message was received.</param>
		/// <param name="socketMessage">The message received.</param>
		static void OnIncomingMessageReceived(std::weak_ptr<IRpcChannel> channel, RpcSocketTransportMessage socketMessage);

		/// <summary>
		/// This method is invoked when a channel has disconnected.
		/// </summary>
		/// <param name="client">The channel that has disconnected.</param>
		static void OnClientDisconnected(std::weak_ptr<IRpcChannel> client);

	private:
		RpcMessageDispatcher();
		RpcMessageDispatcher(const RpcMessageDispatcher& other);
		RpcMessageDispatcher& operator=(const RpcMessageDispatcher& other);

		static void SendRpcMessageFailedMessage(const std::wstring& rpcMessage, RpcSocketTransportMessage socketTransportMessage, HRESULT errorCode, std::weak_ptr<IRpcChannel> client);

		// This method is here so that the unit tests can reset the state of the static
		// members between the run of each test.  That way, the state changes from one
		// test will not affect the other tests.
		static void Reset();
	};

}}}}}